﻿//-----------------------------------------------------------------------
// <copyright file="SP0016 - DoNotDisposeSPSiteAndSPWebReturnedFromSPContextProperties.cs" company="Adrien Siffermann">
//      Copyright © Adrien Siffermann 2008-2009. All rights reserved.
// </copyright>
// <summary>This file contains implementation of the DoNotDisposeSPSiteAndSPWebReturnedFromSPContextProperties class.</summary>
// <website>http://sovfxcoprules.codeplex.com/</website>
//-----------------------------------------------------------------------

namespace SharePoint.FxCop.BestPracticesRules
{
    using System;
    using System.Linq;
    using Microsoft.FxCop.Sdk;

    /// <summary>
    /// Defines a custom Code Analysis rule to check if the
    /// Dispose pattern is respected in the SharePoint Object Model
    /// when using the SPContext.Current.Site, SPContext.Site, SPContext.Current.Web and SPContext.Web properties.
    /// </summary>
    public class DoNotDisposeSPSiteAndSPWebReturnedFromSPContextProperties : BaseStaticAnalysisRule
    {
        #region Private Members
        /// <summary>
        /// Indicates wheter the object is get from the context or not.
        /// </summary>
        private bool gettingFromContext;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DoNotDisposeSPSiteAndSPWebReturnedFromSPContextProperties"/> class.
        /// </summary>
        public DoNotDisposeSPSiteAndSPWebReturnedFromSPContextProperties()
            : base("DoNotDisposeSPSiteAndSPWebReturnedFromSPContextProperties")
        {
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Checks the specified member.
        /// </summary>
        /// <param name="member">The member.</param>
        /// <returns>The collection of problems.</returns>
        public override ProblemCollection Check(Member member)
        {
            if (!BaseStaticAnalysisRule.CheckMember(member))
            {
                return null;
            }

            Method method = member as Method;
            this.gettingFromContext = false;
            this.IsInvoking = false;
            this.VarsToWatch.Clear();

            foreach (Instruction instruction in method.Instructions)
            {
                switch (instruction.OpCode)
                {
                    case OpCode.Stloc_0:
                    case OpCode.Stloc_1:
                    case OpCode.Stloc_2:
                    case OpCode.Stloc_3:
                    case OpCode.Stloc_S:
                        if (this.gettingFromContext)
                        {
                            Local varStored = instruction.Value as Local;
                            this.VarsToWatch.Add(new WatchedVar() { Name = varStored.Name.Name, TypeFullName = varStored.Type.FullName });
                            this.gettingFromContext = false;
                        }

                        break;
                    case OpCode.Ldloc_0:
                    case OpCode.Ldloc_1:
                    case OpCode.Ldloc_2:
                    case OpCode.Ldloc_3:
                    case OpCode.Ldloc_S:
                        Local varLoaded = instruction.Value as Local;
                        WatchedVar watchedVarLoaded = this.VarsToWatch.FirstOrDefault(v => v.Name == varLoaded.Name.Name);
                        if (watchedVarLoaded != null)
                        {
                            this.IsInvoking = true;
                            this.CurrentVar = watchedVarLoaded;
                        }

                        break;
                    case OpCode.Call:
                    case OpCode.Callvirt:
                    case OpCode.Jmp:
                        Method callee = instruction.Value as Method;
                        string calleeName = callee.FullName;
                        if (calleeName.StartsWith("Microsoft.SharePoint.SPContext.get_Site", StringComparison.Ordinal)
                            || calleeName.StartsWith("Microsoft.SharePoint.SPContext.get_Web", StringComparison.Ordinal))
                        {
                            this.gettingFromContext = true;
                        }
                        else if (this.IsInvoking == true)
                        {
                            int j = calleeName.LastIndexOf('.');
                            if (j > 0)
                            {
                                calleeName = calleeName.Substring(j + 1);
                            }

                            if (calleeName == "Dispose")
                            {
                                this.CurrentVar.IsDisposed = true;
                                this.CurrentVar.TargetedNode = instruction;
                            }

                            this.IsInvoking = false;
                        }
                        else
                        {
                            this.gettingFromContext = false;
                        }

                        break;
                    case OpCode.Ldstr:
                        break;
                    default:
                        this.gettingFromContext = false;
                        this.IsInvoking = false;
                        break;
                }
            }

            foreach (var varError in this.VarsToWatch)
            {
                if (varError.IsDisposed)
                {
                    Problems.Add(new Problem(GetResolution(method.FullName, varError.Name), varError.TargetedNode));
                }
            }

            return Problems;
        }
        #endregion
    }
}
